package com.yscp.ysdemo.service.impl;

import com.alibaba.fastjson.JSON;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yscp.ysdemo.common.WordsFilter;
import com.yscp.ysdemo.constant.Constants;
import com.yscp.ysdemo.constant.JWTConstants;
import com.yscp.ysdemo.constant.RedisConstant;
import com.yscp.ysdemo.constant.ResultEnum;
import com.yscp.ysdemo.dto.LoginUser;
import com.yscp.ysdemo.dto.ResponseResult;
import com.yscp.ysdemo.entity.User;
import com.yscp.ysdemo.exception.BusinessException;
import com.yscp.ysdemo.mapper.UserMapper;
import com.yscp.ysdemo.service.LoginService;
import com.yscp.ysdemo.utils.JWTUtils;
import com.yscp.ysdemo.utils.UserHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements LoginService, UserDetailsService {


    @Resource
    private WordsFilter wordsFilter;

    @Resource
    private CaptchaService captchaService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private RedisTemplate redisTemplate;


    /**
     *  该方法由安全框架的认证方法自动调用，本系统的权限设置相对简单，只有普通用户和管理员2个身份，
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername,username);
        User user = userMapper.selectOne(queryWrapper);
        if (Objects.isNull(user)){
            throw new BusinessException(ResultEnum.LOGIN_ERROR);
        }
        List<String> list = new ArrayList<>();
        if (user.getType() == Constants.ADMIN){
            list.add("ADMIN");
        }else if (user.getType() == Constants.USER){
            list.add("USER");
        }
        return new LoginUser(user,list);
    }


    /**
     *  注册验证用户名是否非法（非法内容或者用户名已经存在），AjAX异步
     */
    @Override
    public ResponseResult registeredCheck(String username){
        Map<String,Object> rs = new HashMap<>();
        if(!wordsFilter.checkWord(username)){
            //存在敏感词
            throw new BusinessException(ResultEnum.SENSITIVE_WORDS);
        }
        LambdaQueryWrapper<User> lambdaQueryWrapper  = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getUsername, username);
        User user = userMapper.selectOne(lambdaQueryWrapper);
        if (user != null){
            //用户名已存在
            throw new BusinessException(ResultEnum.REPEAT_REGISTER);
        }
        return  ResponseResult.success();
    }


    /**
     * 注册，存储用户名信息
     * @param user
     * @param captchaParams
     * @return
     */
    @Override
    public ResponseResult registered(User user, String captchaParams) {
        // TODO: 2023/2/23
//        CaptchaVO captchaVO  = JSON.parseObject(captchaParams ,CaptchaVO.class);
//        ResponseModel captchaResponse = captchaService.verification(captchaVO);
//        if(captchaResponse.isSuccess() == false) {
//            throw  new BusinessException(ResultEnum.LOGIN_ERROR);
//        }
        registeredCheck(user.getUsername());
        //记得用和登录相同的加密算法加密
        user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
        userMapper.insert(user);
        return ResponseResult.success("注册成功！");
    }





    /**
     * 验证登录用户合法性，生成token返回给前端，并将用户信息存储到redis
     *  @param user
     * @return
     */
//    * 对于业务错误，到底返回对象还是抛业务异常感觉还是怎么舒服怎么来，
//        * 反正业务异常一样包装成对象返回，不过感觉还是抛异常好点（假如controller层不止
// * 调用一个service，而是controller层后续也存在一些需要service调用成功才进行逻辑，
//        * 那么还是抛异常好点，不然controller还是要多判断一次自己调用的service是否返回正确的结果）
 //抛异常在实际代码编写中会非常整洁
    @Override
    public ResponseResult<Map<String,String>> login(User user,String captchaParams) {
        // TODO: 2023/2/23
//        CaptchaVO captchaVO  = JSON.parseObject(captchaParams ,CaptchaVO.class);
//        ResponseModel captchaResponse = captchaService.verification(captchaVO);
//        if(captchaResponse.isSuccess() == false) {
//            throw  new BusinessException(ResultEnum.LOGIN_ERROR);
//        }
//

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (Objects.isNull(authenticate)){
            throw new BusinessException(ResultEnum.LOGIN_ERROR);
        }
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        Long userId = loginUser.getUser().getUserId();
        Map<String,String> map = new HashMap<>();
        map.put(JWTConstants.USER_ID,userId.toString());
        String token = JWTUtils.getToken(map);
        Map<String,String> result = new HashMap<>();
        result.put("token",token);
        //存入redis
        redisTemplate.opsForValue().set(RedisConstant.getLoginToken(userId.toString()),loginUser,7, TimeUnit.DAYS);
        //登录成功
        return  ResponseResult.success(result,"登录成功");
    }

    /**
     * 登出业务
     * @return
     */
    @Override
    public ResponseResult loginOut() {
        LoginUser loginUser = UserHolder.getLoginUser();
        redisTemplate.delete(RedisConstant.getLoginToken(loginUser.getUser().getUserId().toString()));
        //登出成功
        return  ResponseResult.success(null,"退出登录成功");
    }

}
